//
//  parse.h
//  cobalt
//
//  Created by Nicolas Godbout on 2013-08-21.
//  Copyright (c) 2013 Nicolas Godbout. All rights reserved.
//

#ifndef __cobalt__parse__
#define __cobalt__parse__

#include <list>
#include <locale>
#include <regex>
#include <string>

    /***
     
     ***/

class Parser {
    const char
    *current, *last;
    
    int line;
protected:
    void setInput(const std::string&);
public:
    enum Status {
        OK,     // input was successfully matched and consumed
        ERR,    // input does not match; no input was consumed
        OK_NC,  // empty input was successfully matched
        ERR_C   // no match; some input was consumed and not backtracked
    } status;
    
    Parser();
    Parser(const std::string& input);
    
    int getLine();
    
    char letter();
    char digit();
    char alphaNum();
    char character(char);
    char satisfy(int (*)(int));
    char oneOf(const char[]);
    std::string string(const std::string&);
    void eoln();
    void eof();
    
    typedef std::pair<const char *, const char *> mrange_t;
    std::string match(const std::regex&);
    mrange_t match_range(const std::regex&);
    
    template<class T>
    std::list<T> many(T (Parser::*)());
    
    template<class T,class... A>
    void skipMany(T (Parser::*)(A...), A...);
    
    template<class T,class... A>
    void skipSome(T (Parser::*)(A...), A...);

};

#define ALTS_BEGIN Status saved=status; status=OK_NC;

#define ALT switch(status) { \
    case OK: return ret; \
    case OK_NC: status=saved; return ret; \
    case ERR_C: return {}; \
    case ERR: status=saved; } \
    status=OK_NC;

#define ALTS_END switch(status) { \
    case OK: return ret; \
    case OK_NC: status=saved; return ret; \
    case ERR_C: return {}; \
    case ERR: if (saved==OK) status=ERR_C ;return {}; }

#define _GUARD(FUNC) switch(status) { \
    case OK: if (FUNC) break; else {status=ERR_C; break;} \
    case OK_NC: if (FUNC) break; else {status=ERR; break; } \
    case ERR: case ERR_C: break; }
#define TEST(FUNC) _GUARD(FUNC)

#define NOERR if (status==ERR||status==ERR_C) return {};
#define NOERR_VOID if (status==ERR||status==ERR_C) return;

#define FAIL switch(status) { \
    case OK: status = ERR_C; break; \
    case OK_NC: status = ERR; break; \
    default: break; }

#define _RETURN(X) if (status==OK||status==OK_NC) return X;
#define RETURN(X) _RETURN(X)

inline
Parser::Parser() : line(1) {
//    std::string s{};
    current = nullptr;
    last = nullptr;
}

inline
Parser::Parser(const std::string& input) : line(1) {
    current = input.data();
    last = current + input.length();
}

inline void
Parser::setInput(const std::string &str) {
    current = str.data();
    last = current + str.length();
    line = 1;
}

inline int
Parser::getLine() {
    return line;
}

inline char
Parser::satisfy(int (*func)(int)) {
    NOERR
    TEST( (*func)(*current) )
    NOERR
    status = OK;
    return *current++;
}

inline char
Parser::letter() {
    return satisfy(std::isalpha);
}

inline char
Parser::digit() {
    return satisfy(std::isdigit);
}

inline char
Parser::alphaNum() {
    return satisfy(std::isalnum);
}

inline char
Parser::character(char c) {
    NOERR
    TEST ( *current == c )
    NOERR
    status = OK;
    current++;
    return c;
}

inline char
Parser::oneOf(const char chrs[]) {
    NOERR
    char c = *current;
    int pos = 0;
    bool match = false;
    while ( chrs[pos] ) {
        if ( c == chrs[pos] ) {
            current++;
            match = true;
            break;
        } else
            pos++;
    }
    TEST ( match )
    NOERR
    status = OK;
    return c;
}

inline std::string
Parser::string(const std::string& s) {
    NOERR
//    TEST( !input.compare(s) )   // ## TODO: figure out substring matching
    NOERR
    status = OK;
    return s;
}

inline void
Parser::eoln() {
    NOERR_VOID
    Status saved = status;
    character('\r');
    switch (status) {
        case OK:
            character('\n');
            status = OK;
            line += 1;
            return;
        case ERR:
        case ERR_C:
            status = saved;
            character('\n');
            if (status == OK) line += 1;
            return;
        default:
            abort();
    }
}

inline void
Parser::eof() {
    NOERR_VOID
    if ( current == last ) {
        status = OK_NC;
        return;
    }
    FAIL
}

inline std::string
Parser::match(const std::regex& pattern) {
    NOERR
    std::cmatch match{};
    if ( std::regex_search(current, last, match, pattern, std::regex_constants::match_continuous) ) {
        status = OK;
        current = match[0].second;
        return match[0];
    }
    else {
        FAIL
        return std::string();
    }
}

inline Parser::mrange_t
Parser::match_range(const std::regex& pattern) {
    NOERR
    std::cmatch match{};
    if ( std::regex_search(current, last, match, pattern, std::regex_constants::match_continuous) ) {
        status = OK;
        current = match[0].second;
        return mrange_t(match[0].first, match[0].second);
    }
    else {
        FAIL
        return mrange_t(nullptr, nullptr);
    }
}


template <class T>
inline std::list<T>
Parser::many(T (Parser::*func)()) {
    NOERR
    std::list<T> ret {};
    Status saved = status;
    while ( true ) {
        status = OK_NC;
        T res = (this->*func)();
        if ( status == OK ) {
            ret.push_back(res);
            continue;
        } else if ( status == OK_NC ) {
            abort();    // 'Parser::many' called with a parser matching an empty input
        }
        else
            break;
    }
    if ( status == OK ) {
        if ( ret.empty() )
            status = OK_NC;
        else
            status = saved;
    } else
        return ret;
}

template <class T,class... A>
inline void
Parser::skipMany(T (Parser::*func)(A...), A... args) {
    NOERR_VOID
    bool skipped = false;
    while ( true ) {
        (this->*func)(args...);
        if ( status == OK ) {
            skipped = true;
            continue;
        } else if ( status == OK_NC )
            abort();    // 'Parser::skipMany' called with a parser matching an empty input
        else
            break;
    }
    if ( skipped )
        status = OK;
    else
        status = OK_NC;
    return;
}

template <class T,class... A>
inline void
Parser::skipSome(T (Parser::*func)(A...), A... args) {
    if ( status == ERR || status == ERR_C )
        return;
    
    bool skipped = false;
    while ( true ) {
        (this->*func)(args...);
        if ( status == OK ) {
            skipped = true;
            continue;
        } else if ( status == OK_NC ) {
            abort();
        } else
            break;
    }
    if ( skipped )
        status = OK;
    else
        status = ERR;
    return;
}

int test_parse();

#endif /* defined(__cobalt__parse__) */
